package org.mte.sakn.util;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;


/**
 * DateUtil
 *
 * @author mtraversari
 */
public class DateUtil {

    /**
     * EQUAL_TO
     */
	public static final String E = "=";

    /**
     * GREATER_THAN
     */
	public static final String G = ">";

    /**
     * LESS_THAN
     */
	public static final String L = "<";

    /**
     * GREATER_THAN_OR_EQUAL_TO
     */
	public static final String GE = ">=";

    /**
     * LESS_THAN_OR_EQUAL_TO
     */
	public static final String LE = "<=";

    /**
     * NOT_EQUAL_TO
     */
	public static final String NE = "!=";

	/**
	 * SimpleDateFormat
	 */
	private SimpleDateFormat sdf = null;

	/**
	 * pattern
	 */
	private String pattern = null;

	/**
	 * locale
	 */
	private Locale locale = null;

	/**
	 * DateUtil
	 */
	public DateUtil() {
		initFormatter(null, null);
	}

	/**
	 * DateUtil
	 * @param pattern
	 */
	public DateUtil(String pattern) {
		initFormatter(pattern, null);
	}

	/**
	 * DateUtil
	 * @param pattern
	 * @param locale
	 */
	public DateUtil(String pattern, Locale locale) {
		this.locale = locale;
		initFormatter(pattern, locale);
	}

	/**
	 * initFormatter
	 * @param pattern
	 * @param locale
	 */
	private void initFormatter(String pattern, Locale locale) {
		if (pattern == null && locale == null) {
			sdf = new SimpleDateFormat();
		} else if (pattern != null && locale == null) {
			this.pattern = pattern;
	        sdf = new SimpleDateFormat(pattern);
		} else if (pattern != null && locale != null) {
			this.pattern = pattern;
			this.locale = locale;
			sdf = new SimpleDateFormat(pattern, locale);
		}
		if (sdf != null) {
			sdf.setLenient(false);
		}
	}

	/**
	 * getPattern
	 * @return pattern
	 */
	public String getPattern() {
		return pattern;
	}

	/**
	 * setPattern
	 * @param pattern
	 */
	public void setPattern(String pattern) {
		this.pattern = pattern;
		sdf.applyPattern(pattern);
	}

	/**
	 * getLocale
	 * @return locale
	 */
	public Locale getLocale() {
		return locale;
	}

	/**
	 * Imposta il locale: questo avviene
	 * se il pattern � valorizzato in modo che
	 * l'oggetto "SimpleDateFormat" venga
	 * ristanziato correttamente.
	 *
	 * Se il pattern non � valorizzato non
	 * si ha nessuno effetto
	 */
	public void setLocale(Locale locale) {
		if (pattern != null) {
			this.locale = locale;
			sdf = new SimpleDateFormat(pattern, locale);
			sdf.setLenient(false);
		}
	}

    /**
     * Restiuisce un stringa con la data corrente nel formato specificato
     * nell'oggetto.
     *
     * @return la data corrente.
     */
	public String now() {
		return sdf.format(new Date(System.currentTimeMillis()));
	}

    /**
     * Restiuisce un stringa con la data corrente nel formato specificato.
     *
     * @param pattern � il formato della data.
     * @return la data corrente.
     */
	public String now(String pattern) {
		sdf.applyPattern(pattern);
		return sdf.format(new Date(System.currentTimeMillis()));
	}

    /**
     * Converte una oggetto <code>Date</code> in una stringa secondo il formato
     * dell'oggetto.
     *
     * @param date � la data espressa in <code>Date</code>.
     * @return una stringa rappresentante la data.
     */
	public String getString(Date date) {
		return sdf.format(date);
	}

    /**
     * Converte una oggetto <code>Date</code> in una stringa secondo il formato
     * espresso.
     *
     * @param date � la data espressa in <code>Date</code>.
     * @param pattern � il formato della data.
     * @return una stringa rappresentante la data.
     */
    public String getString(Date date, String pattern) {
    	sdf.applyPattern(pattern);
        return sdf.format(date);
    }

    /**
     * Converte una stringa rappresentante una data in un oggetto
     * di tipo <code>Date</code> secondo il formato dell'oggetto.
     *
     * @param strDate � una stringa rappresentante la data.
     *
     * @return un oggetto di tipo <code>Date</code>.
     * @throws ParseException se si verificano problemi di parse
     */
    public Date getDate(String strDate) throws ParseException {
        return sdf.parse(strDate);
    }

    /**
     * Converte una stringa rappresentante una data in un oggetto
     * di tipo <code>Date</code> secondo il formato espresso.
     *
     * @param strDate � una stringa rappresentante la data.
     * @param pattern � il formato della data espressa in <code>strDate</code>.
     *
     * @return un oggetto di tipo <code>Date</code>.
     * @throws ParseException se si verificano problemi di parse
     */
    public Date getDate(String strDate, String pattern) throws ParseException {
    	sdf.applyPattern(pattern);
        return sdf.parse(strDate);
    }

    /**
     * addSeconds
     * @param date
     * @param seconds
     * @return date
     */
    public static Date addSeconds(Date date, int seconds) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.SECOND, seconds);
        return calendar.getTime();
    }

    /**
     * addMinutes
     * @param date
     * @param minutes
     * @return date
     */
    public static Date addMinutes(Date date, int minutes) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.MINUTE, minutes);
        return calendar.getTime();
    }

    /**
     * addHours
     * @param date
     * @param hours
     * @return date
     */
    public static Date addHours(Date date, int hours) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.HOUR, hours);
        return calendar.getTime();
    }

    /**
     * Aggiunge alla data specificata il numero di giorni indicato.
     *
     * @param date data iniziale.
     * @param days numero giorni.
     * @return date
     */
    public static Date addDays(Date date, int days) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DAY_OF_MONTH, days);
        return calendar.getTime();
    }

    /**
     * addYears
     * @param date
     * @param years
     * @return date
     */
    public static Date addYears(Date date, int years) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.YEAR, years);
        return calendar.getTime();
    }

    /**
     * addMonths
     * @param date
     * @param months
     * @return date
     */
    public static Date addMonths(Date date, int months) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.MONTH, months);
        return calendar.getTime();
    }

    /**
     * <p>Compara due date.<p>
     * <p>Restituisce:</p>
     *
     * <ul>
     *   <li>1 se date1 > date2</li>
     *   <li>0 se date1 = date2</li>
     *   <li>-1 se date1 < date2</li>
     * </ul>
     *
     * @param date1 la prima data
     * @param date2 la seconda data
     * @return -1, 0, 1.
     */
    public static int compare(Date date1, Date date2) {
        Long long1 = new Long(date1.getTime());
        Long long2 = new Long(date2.getTime());
        return long1.compareTo(long2);
    }

	/**
     * Compara due date utilizzando l'operatore specificato.
     * L'operatore � rappresentatato da una stringa.
     *
     * @param date1 la prima data
     * @param operator ha valori possibili in (=, >, <, >=, <=, !=)
     * @param date2 la seconda data
     * @return true se la condizione espressa da
     *          <code>date1</code> <code>operator</code> <code>date2</code> � vera.
	 */
    public static boolean compare(Date date1, String operator, Date date2) {
        boolean result = false;
        if (date1 != null && date2 != null && operator != null) {
            result = evaluate(compare(date1, date2), operator);
        }
        return result;
    }

    /**
     * Valuta l'intero ricavato da una comparazione tra date in funzione
     * dell'operatore specificato.
     *
     * @param result
     * @param operator ha valori possibili in (=, >, <, >=, <=, !=)
     * @return true, false.
     */
    private static boolean evaluate(int result, String operator) {
        boolean isTest0 = (result == 0) &&
        		(operator.equals(E) ||
                 operator.equals(GE) ||
                 operator.equals(LE));


        boolean isTest1 = (result > 0) &&
        				(operator.equals(GE) ||
        				 operator.equals(G));

        boolean isTest2 = (result < 0) &&
        			(operator.equals(LE) ||
                	 operator.equals(L));

        boolean isTest3 = (result > 0 || result < 0) &&
        				  (operator.equals(NE));

        return isTest0 || isTest1 || isTest2 || isTest3;
    }
}
